﻿// ------------------------------------------------------------------------------
// <copyright file="Arc.cs" company="skumaronline.com">
//    Copyright (C) 2013 Suresh Kumar T.
// </copyright>
// ------------------------------------------------------------------------------

namespace StoreAppLib.Graph
{
    using System;
    using System.Windows.Input;
    using Windows.Foundation;
    using Windows.Foundation.Metadata;
    using Windows.UI;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Shapes;

    /// <summary>
    /// Arc shape
    /// </summary>
    [WebHostHidden]
    public sealed class Arc : Path
    {
        /// <summary>
        /// Using a DependencyProperty as the backing store for arc start angle.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty StartAngleProperty =
            DependencyProperty.Register("StartAngle", typeof(double), typeof(Arc), new PropertyMetadata(0.0, Arc.OnAngleChanged));
        
        /// <summary>
        /// Using a DependencyProperty as the backing store for end angle.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty EndAngleProperty =
            DependencyProperty.Register("EndAngle", typeof(double), typeof(Arc), new PropertyMetadata(360.0, Arc.OnAngleChanged));

        /// <summary>
        /// Using a DependencyProperty as the backing store for command to pass tap event to pie chart.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty TapCommandProperty =
            DependencyProperty.Register("TapCommand", typeof(ICommand), typeof(Arc), new PropertyMetadata(default(ICommand)));

        /// <summary>
        /// Variable to hold the path geometry.
        /// </summary>
        private PathGeometry pathGeometry;

        /// <summary>
        /// Variable to hold the path figure.
        /// </summary>
        private PathFigure pathFigure;

        /// <summary>
        /// Variable to hold the arc segment.
        /// </summary>
        private ArcSegment arcSegment;

        /// <summary>
        /// Variable to hold the line segment.
        /// </summary>
        private LineSegment lineSegment;

        /// <summary>
        /// Variable to hold the ellipse geometry.
        /// </summary>
        private EllipseGeometry ellipseGeometry;

        /// <summary>
        /// Variable to hold the size.
        /// </summary>
        private Size actualSize;
        
        /// <summary>
        /// Initializes a new instance of the <see cref="Arc"/> class"
        /// </summary>
        public Arc()
        {           
            this.Stroke = new SolidColorBrush(Colors.White);
            this.StrokeThickness = 2;
            this.Fill = new SolidColorBrush(Colors.Red);
            this.UseLayoutRounding = true;
           
            this.pathGeometry = new PathGeometry();
            this.pathFigure = new PathFigure();

            this.pathFigure.IsClosed = true;
            this.pathFigure.IsFilled = true;

            this.arcSegment = new ArcSegment();
            this.arcSegment.IsLargeArc = false;
            this.arcSegment.SweepDirection = SweepDirection.Clockwise;
            this.lineSegment = new LineSegment();

            this.pathFigure.Segments.Add(this.arcSegment);
            this.pathFigure.Segments.Add(this.lineSegment);

            this.pathGeometry.Figures.Add(this.pathFigure);
            this.Data = this.pathGeometry;

            this.ellipseGeometry = new EllipseGeometry();

            this.Tapped += this.OnArcTapped;
        }

        /// <summary>
        /// Gets or sets the command to pass tap event to pie chart.
        /// </summary>
        /// <value>
        /// The command to pass tap event to pie chart.
        /// </value>
        public ICommand TapCommand
        {
            get { return (ICommand)this.GetValue(TapCommandProperty); }
            set { this.SetValue(TapCommandProperty, value); }
        }

        /// <summary>
        /// Gets or sets the arc start angle.
        /// </summary>
        /// <value>
        /// The arc start angle.
        /// </value>
        public double StartAngle
        {
            get { return (double)this.GetValue(StartAngleProperty); }
            set { this.SetValue(StartAngleProperty, value); }
        }

        /// <summary>
        /// Gets or sets the end angle.
        /// </summary>
        /// <value>
        /// The end angle.
        /// </value>
        public double EndAngle
        {
            get { return (double)this.GetValue(EndAngleProperty); }
            set { this.SetValue(EndAngleProperty, value); }
        }

        /// <summary>
        /// The available size that this object can give to child objects
        /// </summary>
        /// <param name="availableSize">Available size</param>
        /// <returns>Return size</returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            this.actualSize = availableSize;
            this.Refresh();
            return base.MeasureOverride(availableSize);
        }

        /// <summary>
        /// Fires when arc binding property changes
        /// </summary>
        /// <param name="source">The source</param>
        /// <param name="args">The argument</param>
        private static void OnAngleChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
        {
            if (args.NewValue != args.OldValue)
            {
                (source as Arc).Refresh();
            }
        }

        /// <summary>
        /// Event handler for tap
        /// </summary>
        /// <param name="sender">The sender</param>
        /// <param name="args">The argument</param>
        private void OnArcTapped(object sender, TappedRoutedEventArgs args)
        {
            if (this.TapCommand != null)
            {
                this.TapCommand.Execute(this);
            }
        }

        /// <summary>
        /// Refresh arc
        /// </summary>
        private void Refresh()
        {
            Point centre = new Point(this.actualSize.Width / 2, this.actualSize.Height / 2);
            double radius = Math.Min(centre.X - (this.StrokeThickness / 2), centre.Y - (this.StrokeThickness / 2));

            if (radius < 0)
            {
                radius = 0;
            }

            this.lineSegment.Point = centre;
            this.arcSegment.Size = new Size(radius, radius);
            this.pathFigure.StartPoint = this.GetPointAtAngle(this.StartAngle);

            this.arcSegment.Point = this.GetPointAtAngle(this.EndAngle);

            this.arcSegment.IsLargeArc = ((this.EndAngle - this.StartAngle) % 360) > 180;

            this.ellipseGeometry.Center = centre;
            this.ellipseGeometry.RadiusX = radius;
            this.ellipseGeometry.RadiusY = radius;

            if (this.EndAngle - this.StartAngle >= 360)
            {
                this.Data = this.ellipseGeometry;
            }
            else
            {
                this.Data = this.pathGeometry;
            }
        }

        /// <summary>
        /// Gets arc point at the specified angle
        /// </summary>
        /// <param name="angle">The angle</param>
        /// <returns>The arc point</returns>
        private Point GetPointAtAngle(double angle)
        {
            angle = angle % 360;

            Point centre = new Point(this.actualSize.Width / 2, this.actualSize.Height / 2);
            double radius = Math.Min(centre.X - (this.StrokeThickness / 2), centre.Y - (this.StrokeThickness / 2));

            double x = centre.X + (radius * Math.Cos(angle / 180 * Math.PI));
            double y = centre.Y + (radius * Math.Sin(angle / 180 * Math.PI));

            return new Point(x, y);
        }
    }
}